<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>

                    <h4>类和实例</h4>
                    <div class="x-wiki-info"><span>2151次阅读</span></div>
                    <hr style="border-top-color:#ccc" />
                    <div class="x-wiki-content x-content"><p>面向对象最重要的概念就是类（Class）和实例（Instance），必须牢记类是抽象的模板，比如Student类，而实例是根据类创建出来的一个个具体的“对象”，每个对象都拥有相同的方法，但各自的数据可能不同。</p>
<p>仍以Student类为例，在Python中，定义类是通过<code>class</code>关键字：</p>
<pre><code>class Student(object):
    pass
</code></pre><p><code>class</code>后面紧接着是类名，即<code>Student</code>，类名通常是大写开头的单词，紧接着是<code>(object)</code>，表示该类是从哪个类继承下来的，继承的概念我们后面再讲，通常，如果没有合适的继承类，就使用<code>object</code>类，这是所有类最终都会继承的类。</p>
<p>定义好了<code>Student</code>类，就可以根据<code>Student</code>类创建出<code>Student</code>的实例，创建实例是通过类名+()实现的：</p>
<pre><code>&gt;&gt;&gt; bart = Student()
&gt;&gt;&gt; bart
&lt;__main__.Student object at 0x10a67a590&gt;
&gt;&gt;&gt; Student
&lt;class &#39;__main__.Student&#39;&gt;
</code></pre><p>可以看到，变量<code>bart</code>指向的就是一个Student的object，后面的<code>0x10a67a590</code>是内存地址，每个object的地址都不一样，而<code>Student</code>本身则是一个类。</p>
<p>可以自由地给一个实例变量绑定属性，比如，给实例<code>bart</code>绑定一个<code>name</code>属性：</p>
<pre><code>&gt;&gt;&gt; bart.name = &#39;Bart Simpson&#39;
&gt;&gt;&gt; bart.name
&#39;Bart Simpson&#39;
</code></pre><p>由于类可以起到模板的作用，因此，可以在创建实例的时候，把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的<code>__init__</code>方法，在创建实例的时候，就把<code>name</code>，<code>score</code>等属性绑上去：</p>
<pre><code>class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score
</code></pre><p>注意到<code>__init__</code>方法的第一个参数永远是<code>self</code>，表示创建的实例本身，因此，在<code>__init__</code>方法内部，就可以把各种属性绑定到<code>self</code>，因为<code>self</code>就指向创建的实例本身。</p>
<p>有了<code>__init__</code>方法，在创建实例的时候，就不能传入空的参数了，必须传入与<code>__init__</code>方法匹配的参数，但<code>self</code>不需要传，Python解释器自己会把实例变量传进去：</p>
<pre><code>&gt;&gt;&gt; bart = Student(&#39;Bart Simpson&#39;, 59)
&gt;&gt;&gt; bart.name
&#39;Bart Simpson&#39;
&gt;&gt;&gt; bart.score
59
</code></pre><p>和普通的函数相比，在类中定义的函数只有一点不同，就是第一个参数永远是实例变量<code>self</code>，并且，调用时，不用传递该参数。除此之外，类的方法和普通函数没有什么区别，所以，你仍然可以用默认参数、可变参数和关键字参数。</p>
<h3 id="-">数据封装</h3>
<p>面向对象编程的一个重要特点就是数据封装。在上面的Student类中，每个实例就拥有各自的<code>name</code>和<code>score</code>这些数据。我们可以通过函数来访问这些数据，比如打印一个学生的成绩：</p>
<pre><code>&gt;&gt;&gt; def print_score(std):
...     print &#39;%s: %s&#39; % (std.name, std.score)
...
&gt;&gt;&gt; print_score(bart)
Bart Simpson: 59
</code></pre><p>但是，既然Student实例本身就拥有这些数据，要访问这些数据，就没有必要从外面的函数去访问，可以直接在Student类的内部定义访问数据的函数，这样，就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的，我们称之为类的方法：</p>
<pre><code>class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print &#39;%s: %s&#39; % (self.name, self.score)
</code></pre><p>要定义一个方法，除了第一个参数是<code>self</code>外，其他和普通函数一样。要调用一个方法，只需要在实例变量上直接调用，除了<code>self</code>不用传递，其他参数正常传入：</p>
<pre><code>&gt;&gt;&gt; bart.print_score()
Bart Simpson: 59
</code></pre><p>这样一来，我们从外部看Student类，就只需要知道，创建实例需要给出<code>name</code>和<code>score</code>，而如何打印，都是在Student类的内部定义的，这些数据和逻辑被“封装”起来了，调用很容易，但却不用知道内部实现的细节。</p>
<p>封装的另一个好处是可以给Student类增加新的方法，比如<code>get_grade</code>：</p>
<pre><code>class Student(object):
    ...

    def get_grade(self):
        if self.score &gt;= 90:
            return &#39;A&#39;
        elif self.score &gt;= 60:
            return &#39;B&#39;
        else:
            return &#39;C&#39;
</code></pre><p>同样的，<code>get_grade</code>方法可以直接在实例变量上调用，不需要知道内部实现细节：</p>
<pre><code>&gt;&gt;&gt; bart.get_grade()
&#39;A&#39;
</code></pre><h3 id="-">小结</h3>
<p>类是创建实例的模板，而实例则是一个一个具体的对象，各个实例拥有的数据都不相同；</p>
<p>通过在实例变量上调用方法，我们就直接操作了对象内部的数据，但无需知道方法内部的实现细节。</p>
<p>和静态语言不同，Python允许对实例变量绑定任何数据，也就是说，对于两个实例变量，虽然它们都是同一个类的不同实例，但拥有的变量名称都可能不同：</p>
<pre><code>&gt;&gt;&gt; bart = Student(&#39;Bart Simpson&#39;, 59)
&gt;&gt;&gt; lisa=Student(&#39;Lisa Simpson&#39;, 87)
&gt;&gt;&gt; bart.age = 8
&gt;&gt;&gt; bart.age
8
&gt;&gt;&gt; lisa.age
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
AttributeError: &#39;Student&#39; object has no attribute &#39;age&#39;
</code></pre></div>

                    <hr style="border-top-color:#ccc" />

                    